package com.gitee.wsl.compose.ui.bottomsheet

import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.BottomSheetDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.ModalBottomSheetDefaults
import androidx.compose.material3.ModalBottomSheetProperties
import androidx.compose.material3.SheetState
import androidx.compose.material3.contentColorFor
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.node.Ref
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.gitee.wsl.compose.ui.base.BottomSheet

/**
 * @Composable
 * fun OhMyGodIAmAComposable(viewModel: MyViewModel) {
 *     val viewState by viewModel.viewState.collectAsState()
 *     AnimatedBottomSheet(
 *         value = viewState.sheetContent,
 *         onDismissRequest = viewModel::closeSheet,
 *     ) { sheetContent ->
 *         Column(
 *             modifier = Modifier.padding(24.dp),
 *             verticalArrangement = spacedBy(8.dp),
 *         ) {
 *             AnimatedContent(sheetContent) { specialSheetContent ->
 *                 when (specialSheetContent) {
 *                     is SuperSpecialSheetContent.Simple ->
 *                         Text(specialSheetContent.title)
 *
 *                     is SuperSpecialSheetContent.WithButton ->
 *                         Button(onClick = viewModel::onShowOtherSheet) {
 *                             Text(specialSheetContent.buttonText)
 *                         }
 *                 }
 *             }
 *             Button(onClick = viewModel::closeSheet) {
 *                 Text("Close")
 *             }
 *         }
 *     }
 * }
 *
 * 作者：bytebeats
 * 链接：https://juejin.cn/post/7490463554774482980
 * 来源：稀土掘金
 * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
 */

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun <T> BottomSheet.Animated(
    value: T?,
    onDismissRequest: () -> Unit,
    modifier: Modifier = Modifier,
    sheetState: SheetState = rememberModalBottomSheetState(),
    sheetMaxWidth: Dp = BottomSheetDefaults.SheetMaxWidth,
    shape: Shape = BottomSheetDefaults.ExpandedShape,
    containerColor: Color = BottomSheetDefaults.ContainerColor,
    contentColor: Color = contentColorFor(containerColor),
    tonalElevation: Dp = 0.dp,
    scrimColor: Color = BottomSheetDefaults.ScrimColor,
    dragHandle: @Composable (() -> Unit)? = { BottomSheetDefaults.DragHandle() },
    contentWindowInsets: @Composable () -> WindowInsets = { BottomSheetDefaults.windowInsets },
    properties: ModalBottomSheetProperties = ModalBottomSheetDefaults.properties,
    content: @Composable ColumnScope.(T) -> Unit,
) {
    LaunchedEffect(value != null) {
        if (value != null) {
            sheetState.show()
        } else {
            sheetState.hide()
            onDismissRequest()
        }
    }
    if (!sheetState.isVisible && value == null) {
        return
    }
    ModalBottomSheet(
        onDismissRequest = onDismissRequest,
        modifier = modifier,
        sheetState = sheetState,
        sheetMaxWidth = sheetMaxWidth,
        shape = shape,
        containerColor = containerColor,
        contentColor = contentColor,
        tonalElevation = tonalElevation,
        scrimColor = scrimColor,
        dragHandle = dragHandle,
        contentWindowInsets = contentWindowInsets,
        properties = properties,
    ) {
        // Remember the last not null value: If our value becomes null and the sheet slides down,
        // we still need to show the last content during the exit animation.
        val notNullValue = lastNotNullValueOrNull(value) ?: return@ModalBottomSheet
        content(notNullValue)
    }
}

@Composable
fun <T> lastNotNullValueOrNull(value: T?): T? {
    val lastNotNullValueOrNullRef = remember { Ref<T>() }
    return value?.also {
        lastNotNullValueOrNullRef.value = it
    } ?: lastNotNullValueOrNullRef.value
}
